<template xmlns="http://www.w3.org/1999/html">
  <el-dialog
    v-bind:title="'Maintain relationship of ' + testCase.testCaseName"
    :visible.sync="showRelationshipDialog"
    effect="fade"
    :close-on-click-modal="false"
    :before-close="beforeRelationshipDialogClose"
    @open="beforeRelationshipDialogOpen">
    <el-row>
      <el-col :span="5" style="margin-right: 10px">
        <div style="border: 2px solid;">
          <el-row>
            <div class="weakTestcaseBlockShape" style="height: 30px; width: 30px; float: left; margin-left: 10px;"></div>
            <div style="line-height: 50px;margin-left: 50px">single test case</div>
          </el-row>
          <el-row>
            <div class="strongTestcaseBlockShape"
                 style="height: 28px; width: 28px; float: left; margin-left: 10px;"></div>
            <div style="line-height: 50px;margin-left: 50px">with its pre-step</div>
          </el-row>
        </div>
      </el-col>
      <el-col :span="17">
        <el-row>
          <el-col :span="7">
            <div class="row-one">
              test case
            </div>
          </el-col>
          <el-col :span="17">
            <div class="row-one">
              relationship
            </div>
          </el-col>
        </el-row>

        <el-row>
          <el-col :span="7">
            <div style="overflow:scroll;" class="row-two">
              <div shadow="always" v-for="(testCase,index) in testCaseList" v-on:dragstart="drag($event)">
                <el-tooltip effect="dark" v-bind:content="testCase.testCaseName " placement="top-start">
                  <div class="testCaseBlock weakTestcaseBlockShape" :id="testCase.testCaseId" draggable="true"
                       style="position:relative">
                    {{testCase.testCaseName}}
                    <div class="box" @click="boxClick($event)" style="display: none"></div>
                  </div>
                </el-tooltip>
              </div>
              <div shadow="always" v-for="(testCase,index) in testCaseList" v-on:dragstart="drag($event)">
                <el-tooltip effect="dark" v-bind:content="testCase.testCaseName " placement="top-start">
                  <div class="testCaseBlock strongTestcaseBlockShape" draggable="true" :id="'strongTestCase'+testCase.testCaseId" v-show="hasDependency(testCase)"
                       style="position:relative">
                    {{testCase.testCaseName}}
                    <div class="box" @click="boxClick($event)" style="display: none"></div>
                  </div>
                </el-tooltip>
              </div>
            </div>
          </el-col>

          <el-col :span="17">
            <div id="graph" class="row-two" ref="panel" v-on:drop="drop($event)" v-on:dragover="allowDrop($event)">
              <div style="text-align: center; position:absolute; bottom:5px; right: 5px;" >
                <div id="redPoint" class="root" @click="linkToRedPoint">
                  <div class="secondCircle"></div>
                </div>
              </div>
            </div>
          </el-col>
        </el-row>

        <el-row>
          <el-button type="primary" style="background-color: #169bd5; float:left; margin-right: 30px; margin-top: 30px"
                     @click="saveRelationship">Save
          </el-button>
          <el-button type="warning" style="background-color: #ff6600; margin-right: 50px; margin-top: 30px"
                     v-on:click="resetPanel">Reset
          </el-button>
        </el-row>
      </el-col>
    </el-row>
  </el-dialog>

</template>

<script>
  import PlainDraggable from 'plain-draggable';
  import LeaderLine from 'leader-line';

  export default {
    name: 'MaintainRelationship',
    props: {
      testCase: {},
      showRelationshipDialog: false
    },
    data() {
      return {
        testCaseList: [],
        displayDrawLines : new Set(),
        displayDrawNodes : new Set(),
        pointPair : [],
        testCaseMap : {},
        startEndList:[],
        traversedTestCases : new Set(),
        firstTimeOpen : true,
        relationship : {}
      }
    },
    methods: {
      getTestCase: async function () {
        let url = 'testcase/TestCaseList/';
        await this.getTestCaseHttp(url);
      },
      getTestCaseWhenReset: async function () {
        this.testCaseList.splice(0);
        await this.getTestCase();
      },
      getTestCaseHttp: async function (url) {
        let res = await this.$http.get(url)
        if (res.body.status === '400') {
          this.$message({
                          message: res.body.reason,
                          type: 'error'
                        })
        } else {
          this.initTestCaseMap(res.body);
          this.testCaseList = res.body.filter(testCase => testCase.testCaseId !== this.testCase.testCaseId);
        }
      },
      resetPanel: async function() {
        this.clearPanel()

        await this.getTestCaseWhenReset();

        this.initTree(this.relationship);
      },
      clearPanel: function() {
        this.removeDisplayLines()
        Array.from(this.displayDrawNodes).forEach(node => {
          if(node.id !== "redPoint") {
            node.parentElement.removeChild(node)
          }
        });
        this.initDisplayDrawNodesAndLines();
      },
      drag: function (event) {
        event.dataTransfer.setData("Text", event.target.id);
      },
      allowDrop: function (event) {
        event.preventDefault();
      },
      drop: function (event) {
        if (event.target.id === "graph") {
          event.preventDefault();
          var testCaseDomId = event.dataTransfer.getData("Text");
          if (!testCaseDomId.startsWith('strongTestCase')) {
            var plainDraggable = this.moveTestCaseToPanel(testCaseDomId)
            plainDraggable.left = event.pageX - 50;
            plainDraggable.top = event.pageY - 35;
          } else {
            let relationship = this.testCaseMap[testCaseDomId.replace('strongTestCase', '')].relationship
            this.buildRelationshipTree(relationship, null, event.target)
          }
        }
      },
      buildRelationshipTree: function (testCase, parentId) {
        this.moveTestCaseToPanel(testCase.testCaseId)
        if(parentId === null) {
          testCase.dependency.forEach(item => {
            this.buildRelationshipTree(item, testCase.testCaseId)
          });
        } else {
          this.buildLeaderLine(testCase.testCaseId, parentId);
          testCase.dependency.forEach(item => {
            this.buildRelationshipTree(item, testCase.testCaseId)
          });
        }
      },
      getPlainDraggableInstance: function(domElement) {
        var me = this;
        return new PlainDraggable(domElement, {
          onMove: () => {
            Array.from(me.displayDrawLines).forEach(item => item.position())
          },
          zIndex: false
        });
      },
      moveTestCaseToPanel:function(testCaseDomId) {
        var testCaseDom = document.getElementById(testCaseDomId);
        testCaseDom.parentElement.removeChild(testCaseDom)
        this.$refs['panel'].appendChild(testCaseDom);
        testCaseDom.children[0].style = {display: 'block'};
        var plainDraggable = this.getPlainDraggableInstance(testCaseDom)
        var strong = document.getElementById("strongTestCase" + testCaseDomId)
        strong.parentElement.removeChild(strong);
        this.displayDrawNodes.add(testCaseDom);
        return plainDraggable;
      },
      saveRelationship: function () {
        this.traversedTestCases = new Set();
        this.startEndList = Array.from(this.displayDrawLines).map(line => ({start: line.start.id, end: line.end.id}));
        if (this.hasCycle('redPoint')) {
          this.$message({message: 'Relationship can not contain Loop.', type: 'error'});
          return;
        }
        this.relationship = this.buildRelationShip('redPoint');
        var result = this.validateRelationship(this.relationship);
        if (result) {
          this.$emit('hideRelationShipDialog', this.relationship);
          Array.from(this.displayDrawLines).forEach(line => line.hide());
        }
      },
      linkToRedPoint: function () {
        if (this.pointPair.length === 1 ) {
          var line = this.buildLeaderLine(this.pointPair[0], "redPoint");
          line.start.children[0].className = 'box';
        }
      },
      boxClick: function (event) {
        var parentId = event.target.parentElement.id;
        if (this.pointPair.length === 1 && this.pointPair.filter(id => id === parentId).length === 0) {
          var line = this.buildLeaderLine(this.pointPair[0], parentId);
          event.target.className = 'box';
          line.start.children[0].className = 'box';
        } else if (this.pointPair.length === 0) {
          this.pointPair.push(parentId);
          event.target.className = 'box-hover';
        } else if (this.pointPair.length === 1 && this.pointPair.filter(id => id === parentId).length > 0) {
          this.pointPair = [];
          event.target.className = 'box';
        }
        event.stopPropagation();
      },
      buildLeaderLine: function(start, end) {
        var newLine = {};
        var start = document.getElementById(start);
        var end = document.getElementById(end);
        newLine = new LeaderLine(start, end, {color: "red"});
        this.displayDrawLines.add(newLine);
        this.pointPair = [];
        return newLine;
      },
      beforeRelationshipDialogClose: function () {
        this.$confirm('All inputted data will not be saved if you leave, do you want to proceed?', 'warning', {
          type: 'warning'
        }).then(() => {
          this.$emit('hideRelationShipDialog', null);
          this.clearPanel();
        }).catch((e) => {
          //todo 不显示 cancel
          this.$message({message: JSON.stringify(e), type: 'error'});
        });
      },
      beforeRelationshipDialogOpen: function () {
        Array.from(this.displayDrawLines).forEach(line => line.show())
        this.resetPanel();
      },
      removeDialog: function () {
        this.$emit('hideRelationShipDialog');
        Array.from(this.displayDrawLines).forEach(line => line.hide());
      },
      //todo 改算法
      hasCycle: function(node) {
        if (this.traversedTestCases.has(node)) {
          return true;
        }
        this.traversedTestCases.add(node);
        return this.startEndList
          .filter(item => item.end == node)
          .map(item => this.hasCycle(item.start))
          .reduce((x, y) => x || y, false);
      },
      buildRelationShip: function (testCaseId) {
        var me = this;
        var result = {};
        result.dependency = [];
        result.testCaseId = this.testCaseMap[testCaseId].testCaseId;
        var list = [];
        this.startEndList.forEach(item => {
          if (item.end == testCaseId) {
            list.push(item.start);
          }
        });
        list.forEach(item => result.dependency.push(me.buildRelationShip(item)));
        return result;
      },
      initTestCaseMap: function (testCases) {
        testCases.forEach((testCase) => this.testCaseMap[testCase.testCaseId] = testCase)
        this.testCaseMap["redPoint"] = this.testCase;
      },
      validateRelationship: function (relationship) {
        if (this.displayDrawNodes.size > 0 && relationship.dependency.length == 0) {
          this.$message({
                          message: 'Current test case has not been connected in the relationship yet. ',
                          type: 'error'
                        })
          return false;
        }
        return true;
      },
      hasDependency: function (testCase) {
        return testCase.relationship.dependency && testCase.relationship.dependency.length > 0;
      },
      initTree: function(relationship){
        if (relationship.dependency) {
          this.$nextTick(() => {
            relationship.dependency.forEach(item => {
              this.buildRelationshipTree(item, "redPoint", document.getElementById("graph"))
            });
          })
        }
      },
      initDisplayDrawNodesAndLines: function () {
        this.displayDrawLines = new Set();
        this.displayDrawNodes = new Set();
        this.pointPair = [];
        this.startEndList=[];
      },
      removeDisplayLines: function () {
        Array.from(this.displayDrawLines).forEach(line => line.remove());
      }
    },
    mounted() {
      this.getTestCase(null);
      this.initDisplayDrawNodesAndLines()
      this.relationship = this.testCase.relationship;
    },
    destroyed() {
      this.removeDisplayLines()
    }
  }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

  .row-one {
    border: 1px solid black;
    text-align: center;
  }

  .row-two {
    border: 1px solid black;
    height: 500px;
  }

  .weakTestcaseBlockShape {
    margin: 10px auto;
    background: #199ed8;
    width: 100px;
    height: 70px;
    border-radius: 5px;
    text-align: center;
  }

  .strongTestcaseBlockShape {
    margin: 10px auto;
    background: #199ed8;
    width: 100px;
    height: 70px;
    border: 2px solid;
    border-radius: 5px;
    text-align: center;
  }

  .testCaseBlock {
    display: block; /* Fallback for non-webkit */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .box {
    border: black 1px solid;
    background-color: red;
    right: 0px;
    bottom: 0px;
    width: 12px;
    height: 12px;
    position: absolute;
    opacity: 0.8;
    cursor: pointer;
    border-radius: 3px;
  }

  .box-hover {
    border: black 1px solid;
    background-color: green;
    right: 0px;
    bottom: 0px;
    width: 12px;
    height: 12px;
    position: absolute;
    opacity: 0.8;
    cursor: pointer;
    border-radius: 3px;
  }

  .root {
    border-radius: 35px;
    width:50px;
    border:5px solid transparent;
    margin-left:40em;
    height:48px;
    background-color: red;
  }

  .secondCircle {
    width:40px;
    border:4px solid white;
    height:40px;
    margin:0 auto;
    border-radius: 40px;
    background-color: red;
  }
</style>
